[Xcode 12] アプリの起動について変更になった部分まとめ
はじめに
Xcode 12からAppDelegate.swiftとSceneDelegate.swiftファイルを使わないアプリ起動が選べるようになりました。
また、Swift5.3からアプリのエントリーポイントを指定できる@main属性が追加されました。
SE-0281: @main: Type-Based Program Entry Points
今回は、そんなアプリの起動まわりで変更になった部分を、簡単にではありますがまとめてみました。
変更になった部分
Xcode 12でプロジェクトを新規作成するとLifecycleという項目が追加されています。
こちらの選択肢ですが、InterfaceをSwiftUIにするとUIKit App Delegateの他にSwiftUI Appが選べるようになります。
生成されるファイルを比べてみる
それぞれ選択した際に初期に生成されるファイルが違います。
UIKit App Delegate の時は、旧来通りのAppDelegate.swiftとSceneDelegate.swiftファイルが生成されるのに対し、
SwiftUI Appの場合は<プロジェクト名>App.swiftファイルが生成されます。
info.plistを比べてみる
それぞれ生成されたプロジェクトのinfo.plistを比べてみると、Application Scene Manifest という項目の内容が違っています。
SwiftUI Appの場合はEnable Multiple Windows が YES なのに対し、UIKit App Delegateの方は NO になっており、Scene Configurationという項目が追加されています。
試してませんが、info.plistを書き換えることで、最初に選んでないLifecycleの方に変更できそうな感じですね...
コードを比べてみる
次に、それぞれ生成されたコードを見てみます。
UIKit App Delegate (今まで通り)
import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { return true } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { } }
import UIKit import SwiftUI class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let contentView = ContentView() // Use a UIHostingController as window root view controller. if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() } } func sceneDidDisconnect(_ scene: UIScene) { } func sceneDidBecomeActive(_ scene: UIScene) { } func sceneWillResignActive(_ scene: UIScene) { } func sceneWillEnterForeground(_ scene: UIScene) { } func sceneDidEnterBackground(_ scene: UIScene) { } }
@UIApplicationMain が @main になっています。また、SwiftUIの場合はSceneDelegateのfunc scene(_ ... で対象のViewをUIHostingControlleでラップしています。
SwiftUI App
次に、SwiftUI Appを選択した時の<プロジェクト名>App.swiftファイルの中身を見てみます。 ここではLifeCycleSwiftUIというプロジェクト名にしています。
import SwiftUI @main struct LifeCycleSwiftUIApp: App { var body: some Scene { WindowGroup { ContentView() } } }
短くてシンプルになっている印象です。
SwiftUI Appで起動時の処理などが必要な場合
ScenePhaseを使う方法とUIApplicationDelegateAdaptorを使う方法の2パターン存在する様です。
ScenePhase
@Environment(.scenePhase)を追加することにより、アプリのアクティビティ状態を取得することが出来ます。
import SwiftUI @main struct LifeCycleSwiftUIApp: App { @Environment(\.scenePhase) private var scenePhase var body: some Scene { WindowGroup { ContentView() } .onChange(of: scenePhase) { scene in switch scene { case .active: print("scenePhase: active") case .inactive: print("scenePhase: inactive") case .background: print("scenePhase: background") @unknown default: break } } } }
UIApplicationDelegateAdaptor
@UIApplicationDelegateAdaptorを使用することで、既存のAppDelegateを利用することができるようになります。
import SwiftUI @main struct LifeCycleSwiftUIApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { return true } // 必要に応じて処理を追加 }
さいごに
起動時の処理がシンプルなアプリに関しては、LifecycleをSwiftUI Appにするとコードが少なくなって楽だなと思いました。 逆に色々と複雑な対応が必要なアプリに関しては、UIKit App Delegateの方で作った方が良い場合もありそうな印象を受けました。